#ifndef XG_SOCKET_H
#define XG_SOCKET_H
///////////////////////////////////////////////////////////////////
#include "File.h"
#include "ResPool.h"

#ifdef XG_LINUX

#include <arpa/inet.h>

#endif

class Socket;

class HostItem : public Object
{
public:
	int port;
	string host;

	HostItem() : port(0){};
	HostItem(const string& _host, int _port) : host(_host), port(_port){}

	bool canUse() const;
	string toString() const;
	bool equals(const string& host) const;
	bool equals(const string& host, int port) const;
	sp<Socket> getSocket(int timeout = SOCKET_CONNECT_TIMEOUT) const;
};

class ISocket : public IFile
{
private:
	ISocket(const ISocket& sock);
	ISocket& operator = (const ISocket& sock);

protected:
	bool closed;
	SOCKET sock;

public:
	ISocket();
	~ISocket();
	void close();
	bool isClosed() const;
	virtual int tryCheck(int timeout = 0, bool ckrd = true) const;

	SOCKET getHandle() const
	{
		return sock;
	}
	void setCloseFlag(bool flag)
	{
		closed = flag;
	}
};

class Socket : public ISocket
{
private:
	Socket(const Socket& sock);
	Socket& operator = (const Socket& sock);

public:
	Socket(SOCKET sock = INVALID_SOCKET);
	virtual bool init(SOCKET sock = INVALID_SOCKET);
	virtual bool connect(const string& ip, int port, int timeout = SOCKET_CONNECT_TIMEOUT);

	virtual int writeEmptyLine();
	virtual int peek(void* data, int size);
	virtual int readLine(char* data, int size);
	virtual int writeLine(const char* data, int size);
	virtual int read(void* data, int size, bool completed);
	virtual int write(const void* data, int size, bool completed);

	string toString() const;
	bool setSendTimeout(int ms);
	bool setRecvTimeout(int ms);
	HostItem getAddress() const;
	int read(void* data, int size);
	int write(const void* data, int size);

	static bool IsLocalHost(const string& ip);
	static bool IsHostString(const string& str);
	static int GetLocalAddress(vector<string>& vec);
	static string GetHostAddress(const string& host);
};

class ServerSocket : public ISocket
{
private:
	ServerSocket(const ServerSocket& sock);
	ServerSocket& operator = (const ServerSocket& sock);

protected:
	sockaddr_in addr;

public:
	ServerSocket();
	SOCKET accept();
	HostItem getAddress() const;
	bool listen(const string& ip, int port, int backlog = 32, bool reused = true);

	int read(void* data, int size)
	{
		return XG_FAIL;
	}
	int write(const void* data, int size)
	{
		return XG_FAIL;
	}
};

class DgramSocket : public ISocket
{	
private:
	sockaddr_in addr;

	DgramSocket(const DgramSocket& sock);
	DgramSocket& operator = (const DgramSocket& sock);

public:
	DgramSocket();
	HostItem getAddress() const;
	int read(void* data, int size);
	int write(const void* data, int size);
	bool bind(const string& ip, int port);
	bool init(SOCKET sock = INVALID_SOCKET);

	void setAddress(const string& ip, int port)
	{
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		addr.sin_addr.s_addr = inet_addr(ip.c_str());
	}
};

class SocketPool : public ResPool<Socket>
{
protected:
	int port;
	string host;

public:
	SocketPool(const string& host, int port);

public:
	sp<Socket> get()
	{
		sp<Socket> sock = ResPool<Socket>::get();

		if (sock && sock->isClosed())
		{
			disable(sock);

			return get();
		}

		return sock;
	}
	int getPort() const
	{
		return port;
	}
	const string& getHost() const
	{
		return host;
	}

public:
	static void Disable(sp<Socket> sock);
	static string GetKey(const string& host, int port);
	static sp<Socket> Connect(const string& host, int port);

protected:
	static SpinMutex* GetMutex();
	static TSMap<string, sp<SocketPool>>* GetPoolMap();
	static sp<SocketPool> Get(const string& host, int port);
	static sp<SocketPool> Set(const string& host, int port, sp<SocketPool> pool);
};
///////////////////////////////////////////////////////////////////
#endif
